/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "Hogshead.Core.H"
#include "DX11Manager.h"
#include "../EnumConstDeclarations/Sampling.h"

#include "../DataStructures/RenderingTextureBuffer.h"

#include "../HLSLCPPDeclarations/Texture2DMappings.fx"


#include "../Content/Texture.h"

#if USING_HAVOK_ANIM
#include <Common/Base/hkBase.h>
#include <Common/Base/System/hkBaseSystem.h>
#include <Common/Base/Memory/Allocator/Malloc/hkMallocAllocator.h>
#include <Common/Base/Memory/System/Util/hkMemoryInitUtil.h>
#include <Common/Base/Memory/System/hkMemorySystem.h>
#endif //USING_HAVOK_ANIM

#include <d3d11.h>
#include <d3d11sdklayers.h>

#include <D3DX11tex.h>

namespace Hogshead
{
	namespace DX11Rendering
	{
		Logger* DX11Manager::_logger = new Logger("logs/DX11Manager.log");


#pragma region Screen State

		UINT DX11Manager::getWidth()
		{
			return _width;
		}

		UINT DX11Manager::getHeight()
		{
			return _height;
		}

		DX11Manager::FullScreenState DX11Manager::_fullscreen_state = WINDOWED;		
				
		//http://msdn.microsoft.com/en-us/library/ee417025(v=vs.85).aspx
		bool DX11Manager::_finalized = false;
		
		int DX11Manager::max_width;
		int DX11Manager::max_height;
		
		//http://msdn.microsoft.com/en-us/library/bb205075(VS.85).aspx#Debugging
		//http://msdn.microsoft.com/en-us/library/ee417025(v=vs.85).aspx#Full_Screen_Issues
		//http://msdn.microsoft.com/en-us/library/bb205075(VS.85).aspx#Switching
		//http://msdn.microsoft.com/en-us/library/bb173074(v=VS.85).aspx

		int DX11Manager::_desired_screen_resolution_x = 16;
		int DX11Manager::_desired_screen_resolution_y = 16;
		DX11Manager::FullScreenState DX11Manager::_desired_fullscreen_state = WINDOWED;

		int DX11Manager::_screen_change_flags = 0;

		void DX11Manager::preDraw()
		{
			int fullscreen_yn = _screen_change_flags & CHANGE_FULLSCREEN_SETTING;
			int resize_yn = _screen_change_flags & CHANGE_RESOLUTION_SETTING;

			if( fullscreen_yn && resize_yn )
			{
				//set width and height
				_width = _desired_screen_resolution_x;
				_height = _desired_screen_resolution_y;

				if( _desired_fullscreen_state == WINDOWED )
				{
					setFullScreenState(WINDOWED);
					MoveWindow(_window_handle, 0, 0, _width, _height, true);
				}
				else
				{
					//tell the swap chain to fullscreen it
					setFullScreenState(FULLSCREEN);
				}
			}
			else if( fullscreen_yn )
			{
				if( _desired_fullscreen_state == WINDOWED )
				{
					setFullScreenState(WINDOWED);
					MoveWindow(_window_handle, 0, 0, _width, _height, true);
				}
				else
				{
					//calculate max 16:9 size
					_width = max_width;
					_height = (9.0f/16.0f)*_width;

					if( _height > max_height )
					{
						_height = max_height;
						_width = (16.0f/9.0f)*_height;
					}

					//tell the swap chain to fullscreen it
					setFullScreenState(FULLSCREEN);
				}
			}
			else if( resize_yn )
			{
				//set witdth and height
				_width = _desired_screen_resolution_x;
				_height = _desired_screen_resolution_y;

				if( _fullscreen_state == WINDOWED )
				{
					setFullScreenState(WINDOWED);
					MoveWindow(_window_handle, 0, 0, _width, _height, true);
				}
				/*else
				{
					setFullScreenState(FULLSCREEN);
				}*/
				
			}

			//wipe it for the next frame
			_screen_change_flags = NONE;
		}

		void DX11Manager::setDesiredResolution(int in_width, int in_height)
		{
			_screen_change_flags |= CHANGE_RESOLUTION_SETTING;

			_desired_screen_resolution_x = in_width;
			_desired_screen_resolution_y = in_height;
		}

		void DX11Manager::setDesiredScreenState(DX11Manager::FullScreenState in_state)
		{
			_screen_change_flags |= CHANGE_FULLSCREEN_SETTING;

			_desired_fullscreen_state = in_state;
		}

		LRESULT DX11Manager::handleWindowsMessage(HWND in_window_handle, UINT in_message, WPARAM in_word, LPARAM in_long_word)
		{
			if(_finalized)
				return 0;

			switch(in_message)
			{
				//handle size messages we care about
			/*case WM_KILLFOCUS:
				setFullScreenState(_width, _height, WINDOWED);
				return 0;*/

			case WM_ACTIVATE:
				switch(in_word)
				{
					case WA_INACTIVE:
						//ShowWindow(_window_handle, SW_MINIMIZE);
						break;

					case WA_ACTIVE:
					case WA_CLICKACTIVE:
						//ShowWindow(_window_handle, SW_RESTORE);
						//setFullScreenState(_width, _height, _fullscreen_state);
						break;
				}
				return 0;

			case WM_SIZE:

				switch(in_word)
				{
				case SIZE_MINIMIZED:
					//setFullScreenState(WINDOWED);
					break;

				case SIZE_RESTORED:
				case SIZE_MAXIMIZED:					
					//setFullScreenState(_fullscreen_state);
					break;
				}

				resizeScreen();
				return 0;

			case WM_EXITSIZEMOVE: 
				//setFullScreenState(_fullscreen_state);
				//setFullScreenState(_width, _height, _fullscreen_state);
				return 0;

			case WM_KEYDOWN :
				switch(in_word)
				{
					case (WPARAM)'0':
					
						//setFullScreenState(WINDOWED);
						setFullScreenState(WINDOWED);
						return 0;

					case (WPARAM)'9':
						setFullScreenState(FULLSCREEN);
						//setFullScreenState(FULLSCREEN);
						return 0;
				}
			}
			return 0;
		}		
		
		void DX11Manager::setFullScreenState(FullScreenState in_state)
		{
			HRESULT hr;		
			
			_fullscreen_state = in_state;
			
			DXGI_MODE_DESC desc;
			ZeroMemory(&desc, sizeof(DXGI_MODE_DESC));
			desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			desc.Height = _height;
			desc.Width = _width;
			desc.RefreshRate.Numerator = 1;
			desc.RefreshRate.Denominator = 60;
			

			hr = _swap_chain->ResizeTarget( &desc );
			if( hr != S_OK)
				error();
			//set state
			hr = _swap_chain->SetFullscreenState(in_state, NULL);	
			if( hr != S_OK)
				error();
			
			ZeroMemory(&desc, sizeof(DXGI_MODE_DESC));
			desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			desc.Height = _height;
			desc.Width = _width;

			
			hr = _swap_chain->ResizeTarget( &desc );
			if( hr != S_OK)
				error();
		}

		void DX11Manager::resizeScreen()
		{
			HRESULT hr = S_OK;	

			if( _fullscreen_state == WINDOWED )
			{
				RECT rc;
				GetClientRect( _window_handle, &rc );
				_width = rc.right - rc.left;
			    _height = rc.bottom - rc.top;
			}
			else
			{
				//_width = max_width;
				//_height = max_height;
			}

			if( _width < 16 ) _width = 16;
			if( _height < 16 ) _height = 16;

			//release the old back buffer and depth buffer
			_back_buffer_render_target_view->Release();
			_depth_buffer_depth_stencil_view->Release();

			//resize the swap chain
			_swap_chain->ResizeBuffers(2, _width, _height, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
			
			//get the new back buffer
			ID3D11Texture2D* back_buffer_texture;
			hr = _swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*) &back_buffer_texture);
			if(!back_buffer_texture)
				error();

			hr = _d3d11_device->CreateRenderTargetView(back_buffer_texture, NULL, &_back_buffer_render_target_view);
			if( !_back_buffer_render_target_view)
				error();

			back_buffer_texture->Release();

			//create a new depth buffer
			ID3D11Texture2D* depth_buffer_texture;

			hout << "Setting back buffer size: " << (int)_width << ", " << (int)_height << endl;

			D3D11_TEXTURE2D_DESC depth_stencil_descrition;
			ZeroMemory( &depth_stencil_descrition, sizeof(depth_stencil_descrition) );
			depth_stencil_descrition.Width = _width;
			depth_stencil_descrition.Height = _height;
			depth_stencil_descrition.MipLevels = 1;
			depth_stencil_descrition.ArraySize = 1;
			depth_stencil_descrition.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
			depth_stencil_descrition.SampleDesc.Count = multisample_count;
			depth_stencil_descrition.SampleDesc.Quality = multisample_quality;
			depth_stencil_descrition.Usage = D3D11_USAGE_DEFAULT;
			depth_stencil_descrition.BindFlags = D3D11_BIND_DEPTH_STENCIL;
			depth_stencil_descrition.CPUAccessFlags = 0;
			depth_stencil_descrition.MiscFlags = 0;

			hr = _d3d11_device->CreateTexture2D( &depth_stencil_descrition, NULL, &depth_buffer_texture);
			if(!depth_buffer_texture)
				error();

			D3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_description;
			ZeroMemory( &depth_stencil_view_description, sizeof(depth_stencil_view_description) );
			depth_stencil_view_description.Format = depth_stencil_descrition.Format;
			depth_stencil_view_description.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
			depth_stencil_view_description.Texture2D.MipSlice = 0;
			
			hr = _d3d11_device->CreateDepthStencilView( depth_buffer_texture, &depth_stencil_view_description, &_depth_buffer_depth_stencil_view);
			if( !_depth_buffer_depth_stencil_view)
				error();

			depth_buffer_texture->Release();
			
			_d3d11_device_context->ClearState();
			_d3d11_device_context->Flush();
		}

		HWND DX11Manager::_window_handle;

		DWORD DX11Manager::_width; 
		DWORD DX11Manager::_height;	

		/* device, swap chain, and device context */
		IDXGISwapChain* DX11Manager::_swap_chain;
		ID3D11Device* DX11Manager::_d3d11_device;
		ID3D11DeviceContext* DX11Manager::_d3d11_device_context;

		/* feature level array */
		D3D_FEATURE_LEVEL* DX11Manager::_highest_feature_level_capable;	

#pragma endregion

#pragma region Render Target, Shader Resource, and Depth Stencil Control

		/* back buffer render target */
		ID3D11RenderTargetView* DX11Manager::_back_buffer_render_target_view;
		
		/* depth stencil  */
		ID3D11DepthStencilView* DX11Manager::_depth_buffer_depth_stencil_view;

		/* render target array */
		ID3D11RenderTargetView* DX11Manager::_render_targets[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};


		HRESULT DX11Manager::createDepthStencilView(ID3D11Resource* in_texture, const D3D11_DEPTH_STENCIL_VIEW_DESC* in_description, ID3D11DepthStencilView** out_view)
		{
			return _d3d11_device->CreateDepthStencilView(in_texture, in_description, out_view);
		}


		void DX11Manager::clearRenderTarget(ID3D11RenderTargetView* in_render_target_view, const Color& in_clear_color)
		{
			if( !in_render_target_view )
				error("Attempting to clear a NULL RenderTarget");
			//clear the back buffer
			_d3d11_device_context->ClearRenderTargetView(in_render_target_view, in_clear_color);
		}

		void DX11Manager::clearDepthStencil(ID3D11DepthStencilView* in_render_depth_stencil, UINT in_flags, float in_depth, UINT8 in_stencil)
		{
			if( !in_render_depth_stencil )
				error("Attempting to clear a NULL DepthStencil");
			//clear the depth stencil buffer
			_d3d11_device_context->ClearDepthStencilView(in_render_depth_stencil, in_flags, in_depth, in_stencil);
		}

		void DX11Manager::clearBackBuffer(const Color& in_clear_color)
		{
			_d3d11_device_context->ClearRenderTargetView(_back_buffer_render_target_view, in_clear_color);
		}
		
		void DX11Manager::clearDepthBuffer(UINT in_flags, float in_depth, UINT8 in_stencil)
		{
			_d3d11_device_context->ClearDepthStencilView(_depth_buffer_depth_stencil_view, in_flags, in_depth, in_stencil);
		}

		void DX11Manager::setRenderTargetsStandard( const Vector<ID3D11RenderTargetView*>* in_render_target_views, int in_number )
		{

					
			//copy upto 7 over
			for( int i = 0 ;  (i < 7); i++ )
			{
				if( in_render_target_views && i < in_render_target_views->size() )
					_render_targets[i+1] = (*in_render_target_views)[i];
				else
					_render_targets[i+1] = NULL;
			}
			
			//set the backbuffer, we always use RT0 here
			_render_targets[0] = _back_buffer_render_target_view;

			//update OM, the extra 1 is the backbuffer we stored
			_d3d11_device_context->OMSetRenderTargets( in_number+1, (ID3D11RenderTargetView*const*)_render_targets, _depth_buffer_depth_stencil_view );
		}

		void DX11Manager::setRenderTargetsCustom( const Vector<ID3D11RenderTargetView*>* in_render_target_views, int in_number, ID3D11DepthStencilView* in_depth_stencil_view  )
		{
			if(!in_render_target_views)
			{
				for( int rtv_index = 0; rtv_index < 8; rtv_index++)
					_render_targets[rtv_index] = NULL;

				_d3d11_device_context->OMSetRenderTargets( 8, (ID3D11RenderTargetView*const*)_render_targets, in_depth_stencil_view );
				return;
			}
			//copy upto 8 over and copy the depth stencil
			for( int i = 0; (i < in_render_target_views->size()) && (i < 8); i++ )
			{
				_render_targets[i] = (*in_render_target_views)[i];
			}

			//update OM
			_d3d11_device_context->OMSetRenderTargets( in_number, (ID3D11RenderTargetView*const*)_render_targets, in_depth_stencil_view );
		}

		void DX11Manager::setShadowMapWrite( ID3D11DepthStencilView* in_shadow_map_buffer )
		{
			for( int rtv_index = 0; rtv_index < 8; rtv_index++)
					_render_targets[rtv_index] = NULL;

			_d3d11_device_context->OMSetRenderTargets( 8, (ID3D11RenderTargetView*const*)_render_targets, in_shadow_map_buffer);
		}

		

		HRESULT DX11Manager::createRenderTargetView(ID3D11Resource *in_resource, const D3D11_RENDER_TARGET_VIEW_DESC *in_desc, ID3D11RenderTargetView **in_rt_view)
		{
			return DX11Manager::_d3d11_device->CreateRenderTargetView(in_resource, in_desc, in_rt_view);
		}

		HRESULT DX11Manager::createTexture2D(const D3D11_TEXTURE2D_DESC *in_desc, const D3D11_SUBRESOURCE_DATA *in_initial_data, ID3D11Texture2D **in_texture_2d, const char* in_opt_name, int in_opt_length)
		{
			HRESULT hr = DX11Manager::_d3d11_device->CreateTexture2D(in_desc, in_initial_data, in_texture_2d);

			if( hr != S_OK )
				error("Couldn't crate texture");

			if( in_opt_name )
			{
				hr = (*in_texture_2d)->SetPrivateData( WKPDID_D3DDebugObjectName, in_opt_length, in_opt_name );
				if(FAILED(hr))
				{
					StringBuilder sb;
					sb << "ERROR ON LOAD: D3D11Device cannot be named";
					DX11Manager::_logger->write(sb);
					error(sb);
				}
			}
			return hr;
		}

		HRESULT DX11Manager::createTexture1D(const D3D11_TEXTURE1D_DESC *in_desc, const D3D11_SUBRESOURCE_DATA *in_init_data, ID3D11Texture1D** out_texture_1d)
		{
			return _d3d11_device->CreateTexture1D(in_desc, in_init_data, out_texture_1d);
		}

		HRESULT DX11Manager::createShaderResourceView(ID3D11Resource* in_resource, const D3D11_SHADER_RESOURCE_VIEW_DESC* in_desc, ID3D11ShaderResourceView** out_view)
		{
			return _d3d11_device->CreateShaderResourceView(in_resource, in_desc, out_view);
		}

		void DX11Manager::setShaderResources(UINT in_slot, ID3D11ShaderResourceView* const* in_resource_view, ShaderBindFlags in_flags)
		{
			if(in_flags & VERTEX_SHADER)
				setVSShaderResource(in_slot, in_resource_view);	
			if(in_flags & PIXEL_SHADER)
				setPSShaderResource(in_slot, in_resource_view);	
			if(in_flags & GEOMETRY_SHADER)
				setGSShaderResource(in_slot, in_resource_view);	
			if(in_flags & HULL_SHADER)
				setHSShaderResource(in_slot, in_resource_view);
			if(in_flags & DOMAIN_SHADER)
				setDSShaderResource(in_slot, in_resource_view);
		}

		void DX11Manager::setDepthStencilState(bool in_depth_enable, D3D11_DEPTH_WRITE_MASK in_depth_write_mask, D3D11_COMPARISON_FUNC in_comp_func)
		{
			ID3D11DepthStencilState *dss0 = 0;
			ID3D11DepthStencilState *dss1 = 0;
			D3D11_DEPTH_STENCIL_DESC dsd;
			UINT sref;

			_d3d11_device_context->OMGetDepthStencilState(&dss0,&sref);

			if(dss0)
				dss0->GetDesc(&dsd);
			else
			{
				ZeroMemory(&dsd,sizeof(D3D11_DEPTH_STENCIL_DESC));
				sref = 0;
			}

			dsd.DepthEnable = in_depth_enable;
			dsd.DepthWriteMask = in_depth_write_mask;
			dsd.DepthFunc = in_comp_func;

			if(FAILED(_d3d11_device->CreateDepthStencilState(&dsd,&dss1)))
				error("ERROR creating depth stencil state");

			_d3d11_device_context->OMSetDepthStencilState(dss1,sref);

			if(dss0)
				dss0->Release();
			dss1->Release();
		}

		void DX11Manager::setDepthStencilState(ID3D11DepthStencilState* in_depth_stencil_state)
		{
			ID3D11DepthStencilState *dss0 = 0;
			UINT sref;

			_d3d11_device_context->OMGetDepthStencilState(&dss0,&sref);

			_d3d11_device_context->OMSetDepthStencilState(in_depth_stencil_state, sref);
		}

#pragma endregion

#pragma region Lighting

		ID3D11Buffer* DX11Manager::_lighting_buffer = NULL;

		ID3D11Buffer*& DX11Manager::getLightingConstantBuffer()
		{
			return _lighting_buffer;
		}

#pragma endregion

		void DX11Manager::setTopology(D3D11_PRIMITIVE_TOPOLOGY in_topo)
		{
			_d3d11_device_context->IASetPrimitiveTopology( in_topo );
		}

		HRESULT DX11Manager::createConstantBuffer(UINT in_byte_size, ID3D11Buffer** out_constant_buffer_interface)
		{
			//create the description, use the size passed in
			D3D11_BUFFER_DESC cbuffer_desc;
			ZeroMemory(&cbuffer_desc, sizeof(cbuffer_desc));
			cbuffer_desc.Usage = D3D11_USAGE_DEFAULT;
			cbuffer_desc.ByteWidth = in_byte_size;
			cbuffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
			cbuffer_desc.CPUAccessFlags = 0;

			//pass the description to the device to get back an interface
			HRESULT result = _d3d11_device->CreateBuffer(&cbuffer_desc, NULL, out_constant_buffer_interface);
			if( FAILED( result ) )
				error("couldn't create cbuffer");

			return result;
		}

		void DX11Manager::bindConstantBuffer(ID3D11Buffer*const* in_constant_buffer, UINT in_register, ShaderBindFlags in_shader_binds)
		{
			//check the enum to figure out which to bind to then do it
			if( VERTEX_SHADER & in_shader_binds)
				_d3d11_device_context->VSSetConstantBuffers(in_register, 1, in_constant_buffer);

			if( GEOMETRY_SHADER & in_shader_binds)
				_d3d11_device_context->GSSetConstantBuffers(in_register, 1, in_constant_buffer);

			if( PIXEL_SHADER & in_shader_binds)
				_d3d11_device_context->PSSetConstantBuffers(in_register, 1, in_constant_buffer);

			if( HULL_SHADER & in_shader_binds)
				_d3d11_device_context->HSSetConstantBuffers(in_register, 1, in_constant_buffer);

			if( DOMAIN_SHADER & in_shader_binds)
				_d3d11_device_context->DSSetConstantBuffers(in_register, 1, in_constant_buffer);
		}

		

		void DX11Manager::updateConstantBuffer(ID3D11Buffer* in_destination, const void* in_source)
		{
			_d3d11_device_context->UpdateSubresource( in_destination,
				0,
				NULL,
				in_source,
				0,
				0);
		}

		HRESULT DX11Manager::createGeometryShaderWithStreamOutput( const void *in_shader_bytecode,
															 SIZE_T in_bytecode_length,
															 const D3D11_SO_DECLARATION_ENTRY *in_SO_declaration,
															 UINT in_num_entries,
															 const UINT *in_buffer_strides,
															 UINT in_num_strides,
															 UINT in_rasterized_stream,
															 ID3D11ClassLinkage *in_class_linkage,
															 ID3D11GeometryShader **out_geometry_shader)
		{
			return _d3d11_device->CreateGeometryShaderWithStreamOutput(in_shader_bytecode, in_bytecode_length, in_SO_declaration, in_num_entries, 
				in_buffer_strides, in_num_strides, in_rasterized_stream, in_class_linkage, out_geometry_shader);
		}


		void DX11Manager::setShaders(ID3D11VertexShader* in_vertex_shader, 
			ID3D11GeometryShader* in_geometry_shader, ID3D11PixelShader* in_pixel_shader)
		{
			if(in_vertex_shader)
				_d3d11_device_context->VSSetShader(in_vertex_shader, NULL, 0);
			else
				_d3d11_device_context->VSSetShader(NULL, NULL, 0);

			if(in_geometry_shader)
				_d3d11_device_context->GSSetShader(in_geometry_shader, NULL, 0);
			else
				_d3d11_device_context->GSSetShader(NULL, NULL, 0);

			if(in_pixel_shader)
				_d3d11_device_context->PSSetShader(in_pixel_shader, NULL, 0);
			else
				_d3d11_device_context->PSSetShader(NULL, NULL, 0);
		}

		void DX11Manager::setVertexShader(ID3D11VertexShader* in_vertex_shader)
		{
			_d3d11_device_context->VSSetShader(in_vertex_shader, NULL, 0);
		}

		void DX11Manager::setHullShader(ID3D11HullShader* in_hull_shader)
		{
			_d3d11_device_context->HSSetShader(in_hull_shader, NULL, 0);
		}

		void DX11Manager::setDomainShader(ID3D11DomainShader* in_domain_shader)
		{
			_d3d11_device_context->DSSetShader(in_domain_shader, NULL, 0 );
		}

		void DX11Manager::setGeomentryShader(ID3D11GeometryShader* in_geometry_shader)
		{
			_d3d11_device_context->GSSetShader(in_geometry_shader, NULL, 0);
		}

		void DX11Manager::setPixelShader(ID3D11PixelShader* in_pixel_shader)
		{
			_d3d11_device_context->PSSetShader(in_pixel_shader, NULL, 0);
		}

		void DX11Manager::setVertexBuffers(UINT in_start_slot, UINT in_num_buffers, ID3D11Buffer* const* in_vertex_buffer, UINT in_vertex_size, UINT in_offset)
		{
			_d3d11_device_context->IASetVertexBuffers(in_start_slot, in_num_buffers, in_vertex_buffer, &in_vertex_size, &in_offset);
		}
		void DX11Manager::setVertexBuffer(ID3D11Buffer* in_vertex_buffer, UINT in_vertex_size)
		{
			UINT offset = 0;
			_d3d11_device_context->IASetVertexBuffers(0, 1, &in_vertex_buffer, &in_vertex_size, &offset);
		}

		

		HRESULT DX11Manager::initialize(HWND window_handle)
		{
			_screen_change_flags = DX11Manager::NONE;

			!window_handle ? error("You need to hand a Window handle to the DX11Manager") : _window_handle = window_handle;

			HRESULT h_result = S_OK;
			RECT client_rectangle;

			// grab the info about the size of our window
			GetClientRect(_window_handle, &client_rectangle);
			DX11Manager::_width = client_rectangle.right - client_rectangle.left;
			DX11Manager::_height = client_rectangle.bottom - client_rectangle.top;

			max_width = GetSystemMetrics( SM_CXSCREEN );
			max_height = GetSystemMetrics( SM_CYSCREEN );

			//create the swap chain and device
			UINT create_device_flags = 0;//none to start

#ifdef _DEBUG
			create_device_flags |= D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#endif
			DXGI_SWAP_CHAIN_DESC swap_chain_description;
			ZeroMemory( &swap_chain_description, sizeof(swap_chain_description) );
			swap_chain_description.BufferCount = 2;
			swap_chain_description.BufferDesc.Width = DX11Manager::_width;
			swap_chain_description.BufferDesc.Height = DX11Manager::_height;
			swap_chain_description.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			swap_chain_description.BufferDesc.RefreshRate.Numerator = 60;
			swap_chain_description.BufferDesc.RefreshRate.Denominator = 1;
			swap_chain_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
			swap_chain_description.OutputWindow = _window_handle;
			swap_chain_description.SampleDesc.Count = multisample_count;
			swap_chain_description.SampleDesc.Quality = multisample_quality;
			swap_chain_description.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

			DX11Manager::_fullscreen_state == WINDOWED ? swap_chain_description.Windowed = true : swap_chain_description.Windowed = false;
			//swap_chain_description.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //may need to look into this based on what we need

			D3D_DRIVER_TYPE driverTypes[] =
			{
				D3D_DRIVER_TYPE_HARDWARE,
				D3D_DRIVER_TYPE_WARP,
				D3D_DRIVER_TYPE_REFERENCE,
			};
			UINT numDriverTypes = ARRAYSIZE( driverTypes );

			D3D_FEATURE_LEVEL featureLevels[] =
			{
				D3D_FEATURE_LEVEL_11_0,
				D3D_FEATURE_LEVEL_10_1,
				D3D_FEATURE_LEVEL_10_0,
			};
			UINT numFeatureLevels = ARRAYSIZE( featureLevels );

			for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
			{


				D3D_DRIVER_TYPE g_driverType = driverTypes[driverTypeIndex];
				h_result = D3D11CreateDeviceAndSwapChain(NULL,//use default adapter
															g_driverType,//we want hardware
															NULL,//no software dll version
															create_device_flags,//check these out as needed
															featureLevels,//let it attempt to find the best feature level itself
															3,//still letting it deside feature level
															D3D11_SDK_VERSION,//we have this version of the SDK
															&swap_chain_description,//here is what we want
															&_swap_chain,//give us out the swap chain ref
															&_d3d11_device,//give us out the device ref 
															_highest_feature_level_capable,//what we can run, we really aren't using this yet
															&_d3d11_device_context);//give us out the device context ref

				if( SUCCEEDED( h_result ) )
				{
#ifdef _DEBUG
					const char c_szName[] = "d3d device";
					HRESULT hr = _d3d11_device->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(c_szName) - 1, c_szName );
					if(FAILED(hr))
					{
						StringBuilder sb;
						sb << "ERROR ON LOAD: D3D11Device cannot be named";
						DX11Manager::_logger->write(sb);
						error(sb);
					}

					const char device_context_string[] = "device context";
					hr = _d3d11_device_context->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(device_context_string) - 1, device_context_string );
					if(FAILED(hr))
					{
						StringBuilder sb;
						sb << "ERROR ON LOAD: Device context Cannot be named";
						DX11Manager::_logger->write(sb);
						error(sb);
					}
#endif
					break;
				}
			}

			
			if(FAILED( h_result ) ) return h_result;


#ifdef HOGSHEADED
				//create a Factory object and save it off for use by the editor
				IDXGIDevice* dxgi_device;
				h_result = _d3d11_device->QueryInterface(__uuidof(IDXGIDevice), (void**) &dxgi_device);

				if( FAILED( h_result))
					error("couldn't get DXGI device");

				IDXGIAdapter* dxgi_adapter;
				h_result = dxgi_device->GetParent(__uuidof(IDXGIAdapter), (void**) &dxgi_adapter);

				if( FAILED( h_result ) )
					error("couldn't get DXGI adapter");

				h_result = dxgi_adapter->GetParent(__uuidof(IDXGIFactory), (void**) &DX11Manager::_dxgi_factory);

				if( FAILED( h_result ) )
					error("couldn't get DXGI Factory");
#endif



			//create the render target view (back buffer)
			ID3D11Texture2D* back_buffer = 0;
			h_result = _swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&back_buffer);
			

			if(FAILED( h_result ) ) return h_result;
#ifdef _DEBUG
			const char back_buffer_string[] = "back buffer";
			HRESULT hr = back_buffer->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(back_buffer_string) - 1, back_buffer_string );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: Back buffer Cannot be named";
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif
			h_result = _d3d11_device->CreateRenderTargetView(back_buffer, NULL, &_back_buffer_render_target_view);
			
			
			if( FAILED( h_result ) ) return h_result;
#ifdef _DEBUG
			const char render_target_string[] = "back buffer";
			hr = back_buffer->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(render_target_string) - 1, render_target_string );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: render target view buffer Cannot be named";
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif
			back_buffer->Release();

			ID3D11Texture2D* _depth_stencil_texture;

			//create the depth/stencil texture
			D3D11_TEXTURE2D_DESC depth_stencil_descrition;
			ZeroMemory( &depth_stencil_descrition, sizeof(depth_stencil_descrition) );
			depth_stencil_descrition.Width = _width;
			depth_stencil_descrition.Height = _height;
			depth_stencil_descrition.MipLevels = 1;
			depth_stencil_descrition.ArraySize = 1;
			depth_stencil_descrition.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
			depth_stencil_descrition.SampleDesc.Count = multisample_count;
			depth_stencil_descrition.SampleDesc.Quality = multisample_quality;
			depth_stencil_descrition.Usage = D3D11_USAGE_DEFAULT;
			depth_stencil_descrition.BindFlags = D3D11_BIND_DEPTH_STENCIL;
			depth_stencil_descrition.CPUAccessFlags = 0;
			depth_stencil_descrition.MiscFlags = 0;

			h_result = _d3d11_device->CreateTexture2D( &depth_stencil_descrition, NULL, &_depth_stencil_texture);
			if( FAILED( h_result ) ) return h_result;
#ifdef _DEBUG
			const char c_szName[] = "depth stencil texture";
			hr = _depth_stencil_texture->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(c_szName) - 1, c_szName );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: Depth Stencil Cannot be named";
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif
			//create the depth/stencil view 
			D3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_description;
			ZeroMemory( &depth_stencil_view_description, sizeof(depth_stencil_view_description) );
			depth_stencil_view_description.Format = depth_stencil_descrition.Format;
			depth_stencil_view_description.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
			depth_stencil_view_description.Texture2D.MipSlice = 0;
			
			h_result = _d3d11_device->CreateDepthStencilView( _depth_stencil_texture, &depth_stencil_view_description, &_depth_buffer_depth_stencil_view);
			if( FAILED(h_result) ) return h_result;
#ifdef _DEBUG
			const char debug_string[] = "depth stencil view";
			hr = _depth_buffer_depth_stencil_view->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(debug_string) - 1, debug_string );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: Depth Stencil view Cannot be named";
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif			
			_depth_stencil_texture->Release();

			//set it to the output merger stage
			_d3d11_device_context->OMSetRenderTargets(1, &_back_buffer_render_target_view, _depth_buffer_depth_stencil_view);

			//setup the viewport
			D3D11_VIEWPORT viewport;
			viewport.Width = (FLOAT)_width;
			viewport.Height = (FLOAT)_height;
			viewport.MinDepth = 0.0f;
			viewport.MaxDepth = 1.0f;
			viewport.TopLeftX = 0;
			viewport.TopLeftY = 0;
			_d3d11_device_context->RSSetViewports(1, &viewport);



			//create the lighting buffer
			DX11Manager::createConstantBuffer( sizeof(LightingInfo), &_lighting_buffer);
			
#ifdef _DEBUG
			const char Light_debug_string[] = "Lighting Buffer";
			hr = _lighting_buffer->SetPrivateData( WKPDID_D3DDebugObjectName, sizeof(Light_debug_string) - 1, Light_debug_string );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: Lighting Buffer Cannot be named";
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif

			
			return h_result;
		}

		void DX11Manager::setViewport( D3D11_VIEWPORT* in_viewport )
		{
			_d3d11_device_context->RSSetViewports(1, in_viewport);
		}

		void DX11Manager::setPSShaderResource(UINT in_slot, ID3D11ShaderResourceView* const* in_resource_view)
		{
			_d3d11_device_context->PSSetShaderResources(in_slot, 1, in_resource_view);
		}

		void DX11Manager::setGSShaderResource(UINT in_slot, ID3D11ShaderResourceView* const* in_resource_view)
		{
			_d3d11_device_context->GSSetShaderResources(in_slot, 1, in_resource_view);
		}

		void DX11Manager::setVSShaderResource(UINT in_slot, ID3D11ShaderResourceView* const* in_resource_view)
		{
			_d3d11_device_context->VSSetShaderResources(in_slot, 1, in_resource_view);
		}

		void DX11Manager::setHSShaderResource(UINT in_slot, ID3D11ShaderResourceView* const* in_resource_view)
		{
			_d3d11_device_context->HSSetShaderResources(in_slot, 1, in_resource_view);
		}
		
		void DX11Manager::setDSShaderResource(UINT in_slot, ID3D11ShaderResourceView* const * in_resource_view)
		{
			_d3d11_device_context->DSSetShaderResources(in_slot, 1, in_resource_view);
		}

		void DX11Manager::setPSShaderResourcesCustom(ID3D11ShaderResourceView* const* in_shader_resources, UINT in_number, UINT in_start_register)
		{
			_d3d11_device_context->PSSetShaderResources( in_start_register, in_number, in_shader_resources );
		}

		void DX11Manager::setInputLayout(ID3D11InputLayout* in_layout)
		{
			_d3d11_device_context->IASetInputLayout(in_layout);
		}

		void DX11Manager::setSOTargets(UINT in_num_buffers, ID3D11Buffer* const* in_so_targets)
		{
			UINT offset = 0;
			_d3d11_device_context->SOSetTargets(in_num_buffers, in_so_targets, &offset);
		}

		void DX11Manager::updateSubresource(ID3D11Resource* in_resource, const void* in_data, UINT in_source_row)
		{
			_d3d11_device_context->UpdateSubresource(in_resource, 0, NULL, in_data, in_source_row, 0);
		}

		void DX11Manager::present()
		{
			_swap_chain->Present(1, 0);
			//http://forums.create.msdn.com/forums/p/82561/498340.aspx
		}

		HRESULT DX11Manager::loadTexture(Texture* out_texture_resource)
		{
			if( !_d3d11_device ) return S_FALSE;

			HRESULT h_result = D3DX11CreateShaderResourceViewFromFile( _d3d11_device, 
																	out_texture_resource->getPath(),
																	NULL,
																	NULL,
																	&out_texture_resource->_shader_resource_view,
																	NULL);
			if( !FAILED(h_result) )
				return S_OK;
			
			StringBuilder sb;
			sb << "Texture could not be found:" << out_texture_resource->getPath();
			error(sb);

#ifdef _DEBUG
			HRESULT hr = out_texture_resource->_shader_resource_view->SetPrivateData( WKPDID_D3DDebugObjectName, out_texture_resource->getPath().length() - 1, out_texture_resource->getPath() );
			if(FAILED(hr))
			{
				StringBuilder sb;
				sb << "ERROR ON LOAD: Vertex Shader could not be named: " << out_texture_resource->getPath();
				DX11Manager::_logger->write(sb);
				error(sb);
			}
#endif
			return S_FALSE;

		}

		

		HRESULT DX11Manager::createVSFromBlob(ID3DBlob* in_blob, ID3D11VertexShader** out_vs)
		{
			HRESULT h_result = S_OK;
			h_result = _d3d11_device->CreateVertexShader(in_blob->GetBufferPointer(), in_blob->GetBufferSize(), NULL, out_vs);

			return h_result;
		}

		HRESULT  DX11Manager::createGSFromBlob(ID3DBlob* in_blob, ID3D11GeometryShader** out_gs)
		{
			HRESULT h_result = S_OK;
			h_result = _d3d11_device->CreateGeometryShader(in_blob->GetBufferPointer(), in_blob->GetBufferSize(), NULL, out_gs);

			return h_result;
		}

		HRESULT  DX11Manager::createPSFromBlob(ID3DBlob* in_blob, ID3D11PixelShader** out_ps)
		{
			HRESULT h_result = S_OK;
			h_result = _d3d11_device->CreatePixelShader(in_blob->GetBufferPointer(), in_blob->GetBufferSize(), NULL, out_ps);

			return h_result;
		}

		HRESULT DX11Manager::createHSFromBlob( ID3DBlob* in_blob, ID3D11HullShader** out_hs )
		{
			HRESULT hr = S_OK;
			hr = _d3d11_device->CreateHullShader( in_blob->GetBufferPointer(), in_blob->GetBufferSize(), NULL, out_hs );

			return hr;
		}
		
		HRESULT DX11Manager::createDSFromBlob( ID3DBlob* in_blob, ID3D11DomainShader** out_ds )
		{
			HRESULT hr = S_OK;
			hr = _d3d11_device->CreateDomainShader( in_blob->GetBufferPointer(), in_blob->GetBufferSize(), NULL, out_ds );

			return hr;
		}

		HRESULT DX11Manager::createBuffer(D3D11_BUFFER_DESC* in_buffer_description, D3D11_SUBRESOURCE_DATA* in_subresource_data, ID3D11Buffer** out_buffer)
		{
			return DX11Manager::_d3d11_device->CreateBuffer(in_buffer_description, in_subresource_data, out_buffer);
		}

		HRESULT DX11Manager::createRasterizerState(const D3D11_RASTERIZER_DESC* in_desc, ID3D11RasterizerState** in_rasterizer_state)
		{
			return _d3d11_device->CreateRasterizerState(in_desc, in_rasterizer_state);
		}

		HRESULT DX11Manager::createDepthStencilState(const D3D11_DEPTH_STENCIL_DESC* in_desc, ID3D11DepthStencilState** in_depth_stencil_state)
		{
			return _d3d11_device->CreateDepthStencilState(in_desc, in_depth_stencil_state);
		}

		HRESULT DX11Manager::createSamplerState(const D3D11_SAMPLER_DESC* in_desc, ID3D11SamplerState** in_sampler_state)
		{
			return _d3d11_device->CreateSamplerState(in_desc, in_sampler_state);
		}

		HRESULT DX11Manager::createBlendState(const D3D11_BLEND_DESC* in_desc, ID3D11BlendState** in_blend_state)
		{
			return _d3d11_device->CreateBlendState(in_desc, in_blend_state);
		}

		

		void DX11Manager::setRasterizerState(ID3D11RasterizerState* in_rasterizer_state)
		{
			_d3d11_device_context->RSSetState(in_rasterizer_state);
		}

		void DX11Manager::setBlendState(ID3D11BlendState* in_blend_state, const FLOAT in_blend_factor[4], UINT in_sample_mask)
		{
			_d3d11_device_context->OMSetBlendState(in_blend_state, in_blend_factor, in_sample_mask);
		}

		

		void DX11Manager::setSamplers(UINT in_start_slot, UINT in_num_samplers, ID3D11SamplerState* const* in_samplers, ShaderBindFlags in_shader_type)
		{
			if( in_shader_type & GEOMETRY_SHADER )
				_d3d11_device_context->GSSetSamplers(in_start_slot, in_num_samplers, in_samplers);
				
			if( in_shader_type & VERTEX_SHADER )
				_d3d11_device_context->VSSetSamplers(in_start_slot, in_num_samplers, in_samplers);
				
			if( in_shader_type & PIXEL_SHADER )
				_d3d11_device_context->PSSetSamplers(in_start_slot, in_num_samplers, in_samplers);
				
			if( in_shader_type & DOMAIN_SHADER )
				_d3d11_device_context->DSSetSamplers(in_start_slot, in_num_samplers, in_samplers);
				
			if( in_shader_type & HULL_SHADER )
				_d3d11_device_context->HSSetSamplers(in_start_slot, in_num_samplers, in_samplers);
		}

		void DX11Manager::finalize()
		{

			_finalized = true;

			setFullScreenState( WINDOWED );

			//we need to figure out how to release memory
#ifdef HOGSHEADED
			//do any extra cleanup needing to be done by the editor
			DX11Manager::editorCleanup();
#endif

			_depth_buffer_depth_stencil_view->Release();
			_back_buffer_render_target_view->Release();

			_swap_chain->Release();


			//--------------------
			_d3d11_device_context->ClearState();
			_d3d11_device_context->Flush();
			//---------------------
			_d3d11_device_context->Release();

			_d3d11_device->Release();

			delete _logger;

			
		}

		void DX11Manager::draw(int in_num_primitive_topos, int in_start_vertex)
		{
			_d3d11_device_context->Draw(in_num_primitive_topos, in_start_vertex);
		}

		void DX11Manager::drawAuto()
		{
			_d3d11_device_context->DrawAuto();
		}

		ID3D11InputLayout* DX11Manager::createLayout(
			const D3D11_INPUT_ELEMENT_DESC* in_layout_desc,
			UINT in_number_of_elements,
			ID3DBlob* in_vs_blob)
		{
			ID3D11InputLayout* result;

			HRESULT h_result = _d3d11_device->CreateInputLayout(
				in_layout_desc,
				in_number_of_elements,
				in_vs_blob->GetBufferPointer(),
				in_vs_blob->GetBufferSize(),
				&result);

			if( FAILED(h_result ) )
			{
				error("Problem creating input layout");
			}
			return result;
		}

	

#ifdef HOGSHEADED

		IDXGIFactory* DX11Manager::_dxgi_factory = NULL;


		//vector of window handles to use for any editor viewports we make, 4 should be the max we hopefully will ever need
		Vector<DX11Manager::WindowBundle> DX11Manager::_editor_windows;

		

		int DX11Manager::initializeEditorViewport(HWND in_window_handle)
		{
			//create a swap chain for this window and add it to our vector

			if(!in_window_handle)
			{
				error("You need to hand a Window handle to the DX11Manager for a new window!");
			}

			HRESULT hr = S_OK;

			RECT client_rectangle;
			GetClientRect(in_window_handle, &client_rectangle);
			
			WindowBundle wb;
			wb._width = client_rectangle.right - client_rectangle.left;
			wb._height = client_rectangle.bottom - client_rectangle.top;

			//setup the swap chain description
			//create the swap chain and device
			UINT create_device_flags = 0;//none to start

#ifdef _DEBUG
			create_device_flags |= D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#endif
			DXGI_SWAP_CHAIN_DESC swap_chain_description;
			ZeroMemory( &swap_chain_description, sizeof(swap_chain_description) );
			swap_chain_description.BufferCount = 1;
			swap_chain_description.BufferDesc.Width = DX11Manager::_width;
			swap_chain_description.BufferDesc.Height = DX11Manager::_height;
			swap_chain_description.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
			swap_chain_description.BufferDesc.RefreshRate.Numerator = 60;
			swap_chain_description.BufferDesc.RefreshRate.Denominator = 1;
			swap_chain_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
			swap_chain_description.OutputWindow = in_window_handle;
			swap_chain_description.SampleDesc.Count = multisample_count;
			swap_chain_description.SampleDesc.Quality = multisample_quality;
			swap_chain_description.Windowed = true;
			//swap_chain_description.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //may need to look into this based on what we need

			//hand the description and device to factory to get a swap chain.
			hr = _dxgi_factory->CreateSwapChain( DX11Manager::_d3d11_device, &swap_chain_description, &wb._swap_chain );

			if( FAILED( hr ))
				error("Couldn't create swap chain.");

			//create the render target view( back buffer)
			ID3D11Texture2D* back_buffer_texture = NULL;
			hr = wb._swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&back_buffer_texture);

			if( FAILED( hr ) )
				error("Couldn't get back buffer.");

			hr = _d3d11_device->CreateRenderTargetView(back_buffer_texture, NULL, &wb._render_target);
			if( FAILED( hr ) )
				error("Couldn't create the render target view.");

			//add the bundle
			DX11Manager::_editor_windows.add( wb );

			return DX11Manager::_editor_windows.size() - 1;
		}

		void DX11Manager::editorCleanup()
		{
			for( int i = 0; i < DX11Manager::_editor_windows.size(); i++ )
				DX11Manager::_editor_windows[i].finalize();
		}

		//http://www.gamedev.net/community/forums/topic.asp?topic_id=510748
		//http://www.gamedev.net/community/forums/topic.asp?topic_id=564818
#endif


	}


	
}
